<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>Drag and Drop in Swing</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Java, Swing, drag and drop, tutorial, programming, Linux">
<meta name="description" content="This part of the Java Swing tutorial covers drag and drop 
operations.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href=".." title="Home">Contents</a>


<h1>Drag and Drop in Swing</h1>


<p>
In computer graphical user interfaces, drag-and-drop is the action of 
(or support for the action of) clicking on a virtual object and dragging 
it to a different location or onto another virtual object. In general, it 
can be used to invoke many kinds of actions, or create various types of 
associations between two abstract objects. (Wikipedia)
</p>

<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* NewSquare */
google_ad_slot = "0364418177";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script>

<p>
Drag and drop functionality is one of the most visible aspects of the graphical 
user interface. Drag and drop operation enables users to do complex things 
intuitively. 
</p>

<p>
Usually, we can drag and drop two things. Data or some graphical objects. 
If we drag an image from one application to another, we drag and drop binary 
data. If we drag a tab in Firefox and move it to another place, we drag and drop a 
graphical component. 
</p>

<p>
The sheer amount of various classes involved with drag and drop operations 
in Java Swing toolkit might be overwhelming.
The best way how to cope with this complexity is to create a small example 
for all situations. And slowly make it to more complex examples. 
</p>

<img src="/img/gui/javaswing/dragdrop1.png" alt="Drag and drop">

<p>
The component, where the drag operation begins must have a <b>DragSource</b> 
object registered. A <b>DropTarget</b> is an object responsible for accepting 
drops in an drag and drop operation. A <b>Transferable</b> encapsulates data 
being transferred. The transferred data can be of various type. A <b>DataFlavor</b> 
object provides information about the data being transferred.
</p>


<p>
Several Swing components have already a built-in support for drag and drop
operations. In such cases, a Swing programmer
uses a <b>TransferHandler</b> to manage the drag and drop functionality. 
In situations, where there is no built-in support, the programmer has to 
create everything from scratch.
</p>


<h2>A simple drag and drop example</h2>

<p>
We will demonstrate a simple drag and drop example. We will work with 
built-in drag and drop support. We will 
utilize a <b>TransferHandler</b> class. 
</p>


<pre class="code">
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.TransferHandler;


public class SimpleDnD extends JFrame {

    JTextField field;
    JButton button;

    public SimpleDnD() {

        setTitle("Simple Drag & Drop");

        setLayout(null);

        button = new JButton("Button");
        button.setBounds(200, 50, 90, 25);

        field = new JTextField();
        field.setBounds(30, 50, 150, 25);

        add(button);
        add(field);

        field.setDragEnabled(true);
        button.setTransferHandler(new TransferHandler("text"));

        setSize(330, 150);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public static void main(String[] args) {
        new SimpleDnD();
    }
}
</pre>

<p>
In our example we have a text field and a button. We can drag a text from the 
field and drop it onto the button.
</p>

<pre class="explanation">
field.setDragEnabled(true);
</pre>

<p>
The text field has a built in support for dragging. We must enable it.
</p>

<pre class="explanation">
button.setTransferHandler(new TransferHandler("text"));
</pre>

<p>
The <code>TransferHandler</code> is a class responsible for 
transfering data between components. 
The constructor takes a property name as a parameter.
</p>


<img src="/img/gui/javaswing/simplednd.png" alt="Simple drag &amp; drop example">
<div class="figure">Figure: Simple drag &amp; drop example</div>


<h2>Icon drag &amp; drop</h2>

<p>
Some of the Java Swing components do not have built in drag support. 
<code>JLabel</code> component is such a component. We have 
to code the drag functionality ourselves.
</p>

<p>
We will drag and drop icons. In the previous example, we used a text property. 
This time we will use an icon property.
</p>

<pre class="code">
import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.TransferHandler;


public class IconDnD extends JFrame {


    public IconDnD() {

        setTitle("Icon Drag & Drop");

        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 50, 15));

        ImageIcon icon1 = new ImageIcon("sad.png");
        ImageIcon icon2 = new ImageIcon("plain.png");
        ImageIcon icon3 = new ImageIcon("crying.png");

        JButton button = new JButton(icon2);
        button.setFocusable(false);

        JLabel label1  = new JLabel(icon1, JLabel.CENTER);
        JLabel label2  = new JLabel(icon3, JLabel.CENTER);

        MouseListener listener = new DragMouseAdapter();
        label1.addMouseListener(listener);
        label2.addMouseListener(listener);

        label1.setTransferHandler(new TransferHandler("icon"));
        button.setTransferHandler(new TransferHandler("icon"));
        label2.setTransferHandler(new TransferHandler("icon"));

        panel.add(label1);
        panel.add(button);
        panel.add(label2);
        add(panel);

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    class DragMouseAdapter extends MouseAdapter {
        public void mousePressed(MouseEvent e) {
            JComponent c = (JComponent) e.getSource();
            TransferHandler handler = c.getTransferHandler();
            handler.exportAsDrag(c, e, TransferHandler.COPY);
        }
    }

    public static void main(String[] args) {
        new IconDnD();
    }
}
</pre>

<p>
In the code example, we have two labels and a button. Each component displays an icon. 
The two labels enable drag
gestures, the button accepts a drop gesture. 
</p>

<pre class="explanation">
MouseListener listener = new DragMouseAdapter();
label1.addMouseListener(listener);
label2.addMouseListener(listener);
</pre>

<p>
The drag support is not enabled by default for the label. We register a 
custom mouse adapter for both labels.
</p>

<pre class="explanation">
label1.setTransferHandler(new TransferHandler("icon"));
button.setTransferHandler(new TransferHandler("icon"));
label2.setTransferHandler(new TransferHandler("icon"));
</pre>

<p>
Each of the three components has a <code>TransferHandler</code> 
class for an icon property.
The <code>TransferHandler</code> is needed for both drag 
sources and drag targets as well.
</p>

<pre class="explanation">
JComponent c = (JComponent) e.getSource();
TransferHandler handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.COPY);
</pre>

<p>
These code lines initiate the drag support. We get the drag source. 
In our case it is a label instance.
We get it's transfer handler object. And finally initiate the 
drag support with the <code>exportAsDrag()</code>
method call.
</p>


<img src="/img/gui/javaswing/icondnd.png" alt="Icon drag & drop example">
<div class="figure">Figure: Icon drag &amp; drop example</div>


<h2>Custom JList drop example</h2>

<p>
Some components do not have a default drop support. One of them is a JList 
component. There is a good reason for this.
We don't know, if the data will be inserted into one row, or two or more 
rows. So we must implement manually the
drop support for the list component. 
</p>

<p>
The comma separated text will be inserted into two or more rows. Text without
 a comma will go into one row. 
</p>

<pre class="code">
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;

import javax.swing.DefaultListModel;
import javax.swing.DropMode;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.TransferHandler;


public class ListDrop extends JFrame {

    JTextField field;
    DefaultListModel model;

    public ListDrop() {

        setTitle("ListDrop");

        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 15, 15));

        JScrollPane pane = new JScrollPane();
        pane.setPreferredSize(new Dimension(180, 150));

        model = new DefaultListModel();
        JList list = new JList(model);

        list.setDropMode(DropMode.INSERT);
        list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        list.setTransferHandler(new ListHandler());

        field = new JTextField("");
        field.setPreferredSize(new Dimension(150, 25));
        field.setDragEnabled(true);

        panel.add(field);
        pane.getViewport().add(list); 
        panel.add(pane);

        add(panel);

        pack();

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }


    private class ListHandler extends TransferHandler {
        public boolean canImport(TransferSupport support) {
             if (!support.isDrop()) {
                 return false;
             }

             return support.isDataFlavorSupported(DataFlavor.stringFlavor);
         }

         public boolean importData(TransferSupport support) {
             if (!canImport(support)) {
               return false;
             }

             Transferable transferable = support.getTransferable();
             String line;
             try {
               line = (String) transferable.getTransferData(DataFlavor.stringFlavor);
             } catch (Exception e) {
               return false;
             }

             JList.DropLocation dl = (JList.DropLocation) support.getDropLocation();
             int index = dl.getIndex();

             String[] data = line.split(",");
             for (String item: data) {
                 if (!item.isEmpty())
                    model.add(index++, item.trim());
             }
             return true;
         }
    }

    public static void main(String[] args) {
        new ListDrop();
    }
}
</pre>

<p>
In the above example, we have a text field and a list component. The text 
in the text field can be dragged and
dropped into the list. If the text is comma separated, the words will be 
split into rows. If not, the text will be
inserted into one row.
</p>


<pre class="explanation">
list.setDropMode(DropMode.INSERT);
</pre>

<p>
Here we specify a drop mode. The <code>DropMode.INSERT</code> specifies, 
that we are going to insert new items
into the list component. If we chose <code>DropMode.INSERT</code>, 
we would drop new items onto the existing ones.
</p>

<pre class="explanation">
list.setTransferHandler(new ListHandler());
</pre>

<p>
We set a custom transfer handler class. 
</p>

<pre class="explanation">
field.setDragEnabled(true);
</pre>

<p>
We enable the drag support for the text field component. 
</p>

<pre class="explanation">
public boolean canImport(TransferSupport support) {
    if (!support.isDrop()) {
        return false;
    }
    return support.isDataFlavorSupported(DataFlavor.stringFlavor);
}
</pre>

<p>
This method tests suitability of a drop operation. Here we filter out the 
clipboard paste operations and
allow only String drop operations. If the method returns false, the drop 
operation is cancelled.
</p>

<pre class="explanation">
public boolean importData(TransferSupport support) {
...
}
</pre>

<p>
The <code>importData()</code> method transfers the data from the 
clipboard or from the drag and drop operation to the drop location.
</p>

<pre class="explanation">
Transferable transferable = support.getTransferable();
</pre>

<p>
The <code>Transferable</code> is the class, where the data is bundled. 
</p>

<pre class="explanation">
line = (String) transferable.getTransferData(DataFlavor.stringFlavor);
</pre>

<p>
We retrieve our data. 
</p>

<pre class="explanation">
JList.DropLocation dl = (JList.DropLocation) support.getDropLocation();
int index = dl.getIndex();
</pre>

<p>
We get a drop location for the list. We retrieve the index, where 
the data will be inserted.
</p>

<pre class="explanation">
String[] data = line.split(",");
for (String item: data) {
    if (!item.isEmpty())
        model.add(index++, item.trim());
}
</pre>

<p>
Here we split the text into parts and insert it into one or more rows.
</p>

<img src="/img/gui/javaswing/listdrop.png" alt="JList drop example">
<div class="figure">Figure: JList drop example</div>


<p>
The previous examples used components with built-in drag and drop support. 
Next we are going to create
a drag and drop functionality from scratch. 
</p>


<h2>Drag Gesture</h2>

<p>
In the following example we will inspect a simple drag gesture.
 We will work with several classes needed to create 
a drag gesture. A <b>DragSource</b>, <b>DragGestureEvent</b>, 
<b>DragGestureListener</b>, <b>Transferable</b>.
</p>

<pre class="code">
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class DragGesture extends JFrame implements 
    DragGestureListener, Transferable {

    public DragGesture() {

        setTitle("Drag Gesture");

        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 50, 15));

        JPanel left = new JPanel();
        left.setBackground(Color.red);
        left.setPreferredSize(new Dimension(120, 120));

        DragSource ds = new DragSource();
        ds.createDefaultDragGestureRecognizer(left,
            DnDConstants.ACTION_COPY, this);

        panel.add(left);
        add(panel);

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public void dragGestureRecognized(DragGestureEvent event) {
        System.out.println("grag gesture");
        Cursor cursor = null;
        if (event.getDragAction() == DnDConstants.ACTION_COPY) {
            cursor = DragSource.DefaultCopyDrop;
        }
        event.startDrag(cursor, this);
    }

    public static void main(String[] args) {
        new DragGesture();
    }

    public Object getTransferData(DataFlavor flavor) {
        return null;
    }

    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[0];
    }

    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return false;
    }
}
</pre>


<p>
This simple example demostrates a drag gesture. The drag gesture is 
created, when we click on a component and move
a mouse pointer, while the button is pressed. The example will show, 
how we can create a DragSource for a component.
</p>

<pre class="explanation">
public class DragGesture extends JFrame implements 
   DragGestureListener, Transferable {
</pre>

<p>
The DragGesture implements two interfaces. The <code>DragGestureListener</code> 
will listen for drag gestures. The <code>Transferable</code> handles 
data for a transfer operation. In the example, we will not 
transfer any data. We will only demonstrate a drag gesture. So the three necessary 
methods of the Transferable 
interface are left unimplemented.
</p>

<pre class="explanation">
DragSource ds = new DragSource();
ds.createDefaultDragGestureRecognizer(left,
    DnDConstants.ACTION_COPY, this);
</pre>

<p>
Here we create a <code>DragSource</code> object and register 
it for the left panel. 
The DragSource is the entity responsible for the initiation of the 
Drag and Drop operation. 
The <code>createDefaultDragGestureRecognizer()</code> associates 
a drag source and <code>DragGestureListener</code> with a particular component.
</p>

<pre class="explanation">
public void dragGestureRecognized(DragGestureEvent event) {

}
</pre>

<p>
The <code>dragGestureRecognized()</code> method responds to a drag gesture. 
</p>

<pre class="explanation">
Cursor cursor = null;
if (event.getDragAction() == DnDConstants.ACTION_COPY) {
    cursor = DragSource.DefaultCopyDrop;
}
event.startDrag(cursor, this);
</pre>

<p>
The <code>startDrag()</code> method of the DragGestureEvent 
finally starts the drag operation. We will specify two
parameters. The cursor type and the Transferable object. 
</p>

<pre class="explanation">
public Object getTransferData(DataFlavor flavor) {
    return null;
}

public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[0];
}

public boolean isDataFlavorSupported(DataFlavor flavor) {
    return false;
}
</pre>

<p>
The object that implements the Transferable interface must implement these 
three methods. As I have already mentioned, we left these methods unimplemented for now.
</p>


<h2>A complex drag and drop example</h2>

<p>
In the following example, we create a complex drag and drop example. 
We create a drag source a drop target and a transferable object. 
</p>

<pre class="code">
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;


public class ComplexExample extends JFrame 
    implements DragGestureListener {

    JPanel panel;
    JPanel left;

    public ComplexExample() {

        setTitle("Complex Example");

        panel  = new JPanel(new FlowLayout(FlowLayout.LEFT, 50, 15));

        JButton openb = new JButton("Choose Color");
        openb.setFocusable(false);

        left = new JPanel();
        left.setBackground(Color.red);
        left.setPreferredSize(new Dimension(100, 100));

        openb.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                JColorChooser clr = new JColorChooser();
                Color color = clr.showDialog(panel, "Choose Color", Color.white);
                left.setBackground(color);
            }
        });

        JPanel right = new JPanel();
        right.setBackground(Color.white);
        right.setPreferredSize(new Dimension(100, 100));

        new MyDropTargetListener(right);

        DragSource ds = new DragSource();
        ds.createDefaultDragGestureRecognizer(left,
            DnDConstants.ACTION_COPY, this);

        panel.add(openb);
        panel.add(left);
        panel.add(right);
        add(panel);

        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public void dragGestureRecognized(DragGestureEvent event) {
        Cursor cursor = null;
        JPanel panel = (JPanel) event.getComponent();

        Color color = panel.getBackground();

        if (event.getDragAction() == DnDConstants.ACTION_COPY) {
            cursor = DragSource.DefaultCopyDrop;
        }

        event.startDrag(cursor, new TransferableColor(color));
    }

    class MyDropTargetListener extends DropTargetAdapter {

        private DropTarget dropTarget;
        private JPanel panel;

     public MyDropTargetListener(JPanel panel) {
        this.panel = panel;

        dropTarget = new DropTarget(panel, DnDConstants.ACTION_COPY, 
            this, true, null);
      }


      public void drop(DropTargetDropEvent event) {
        try {

          Transferable tr = event.getTransferable();
          Color color = (Color) tr.getTransferData(TransferableColor.colorFlavor);

            if (event.isDataFlavorSupported(TransferableColor.colorFlavor)) {

              event.acceptDrop(DnDConstants.ACTION_COPY);
              this.panel.setBackground(color);
              event.dropComplete(true);
              return;
            }
          event.rejectDrop();
        } catch (Exception e) {
          e.printStackTrace();
          event.rejectDrop();
        }
      }
    }

    public static void main(String[] args) {
        new ComplexExample();
    }
}


class TransferableColor implements Transferable {
 
    protected static DataFlavor colorFlavor =
        new DataFlavor(Color.class, "A Color Object");

    protected static DataFlavor[] supportedFlavors = {
        colorFlavor,
        DataFlavor.stringFlavor,
    };

    Color color;

    public TransferableColor(Color color) { this.color = color; }

    public DataFlavor[] getTransferDataFlavors() { return supportedFlavors; }

    public boolean isDataFlavorSupported(DataFlavor flavor) {
    if (flavor.equals(colorFlavor) || 
        flavor.equals(DataFlavor.stringFlavor)) return true;
    return false;
  }


   public Object getTransferData(DataFlavor flavor) 
        throws UnsupportedFlavorException
   {
     if (flavor.equals(colorFlavor))
         return color;
     else if (flavor.equals(DataFlavor.stringFlavor)) 
         return color.toString();
     else 
         throw new UnsupportedFlavorException(flavor);
   }
}
</pre>

<p>
The code example shows a button and two panels. The button displays a 
color chooser dialog and sets a color
for the first panel. The color can be dragged into the second panel.
</p>

<p>
This example will enhance the previous one. We will add a drop target 
and a custom transferable object. 
</p>

<pre class="explanation">
new MyDropTargetListener(right);
</pre>

<p>
We register a drop target listener with the right panel. 
</p>

<pre class="explanation">
event.startDrag(cursor, new TransferableColor(color));
</pre>

<p>
The <code>startDrag()</code> method has two parameters. A cursor 
and a <code>Transferable</code> object. 
</p>

<pre class="explanation">
public MyDropTargetListener(JPanel panel) {
    this.panel = panel;

    dropTarget = new DropTarget(panel, DnDConstants.ACTION_COPY, 
        this, true, null);
}
</pre>

<p>
In the MyDropTargetListener we create a drop target object. 
</p>

<pre class="explanation">
Transferable tr = event.getTransferable();
Color color = (Color) tr.getTransferData(TransferableColor.colorFlavor);

if (event.isDataFlavorSupported(TransferableColor.colorFlavor)) {

    event.acceptDrop(DnDConstants.ACTION_COPY);
    this.panel.setBackground(color);
    event.dropComplete(true);
    return;
}
</pre>

<p>
We get the data being transferred. In our case it is a color object. 
Here we set the color of the right panel.
</p>

<pre class="explanation">
event.rejectDrop();
</pre>

<p>
If the conditions for a drag and drop operation are not 
fulfilled, we reject it. 
</p>

<pre class="explanation">
protected static DataFlavor colorFlavor =
    new DataFlavor(Color.class, "A Color Object");
</pre>

<p>
In the TransferableColor, we create a new <code>DataFlavor</code> object. 
</p>

<pre class="explanation">
protected static DataFlavor[] supportedFlavors = {
    colorFlavor,
    DataFlavor.stringFlavor,
};
</pre>

<p>
Here we specify, what data flavors we support. In our case it is a 
custom defined color flavor and a pre-defined
<code>DataFlavor.stringFlavor</code>.
</p>

<pre class="explanation">
public Object getTransferData(DataFlavor flavor) 
     throws UnsupportedFlavorException
{
  if (flavor.equals(colorFlavor))
      return color;
  else if (flavor.equals(DataFlavor.stringFlavor)) 
      return color.toString();
  else 
      throw new UnsupportedFlavorException(flavor);
}
</pre>

<p>
Return an object for a specific data flavor.
</p>


<img src="/img/gui/javaswing/complexexample.png" alt="A complex example">
<div class="figure">Figure: A complex example</div>


<p>
This part of the Java Swing tutorial was dedicated to Swing drap and drop operations. 
</p>

<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>
<br>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified November 18, 2007  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>